/**
 *  Copyright 2010 Kyle O'Connor, Justin Arling, Mike Mercieca
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package edu.drexel.sd0910.ece01.aqmon.gui.wizard.impl;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

import org.apache.log4j.Logger;

import edu.drexel.sd0910.ece01.aqmon.config.ConfigKeys;
import edu.drexel.sd0910.ece01.aqmon.data.ActiveNode;
import edu.drexel.sd0910.ece01.aqmon.gui.DeploymentNameCustomDialog;
import edu.drexel.sd0910.ece01.aqmon.gui.GUIUpdates;
import edu.drexel.sd0910.ece01.aqmon.gui.MainModel;
import edu.drexel.sd0910.ece01.aqmon.manager.FileOutputManager;
import edu.drexel.sd0910.ece01.aqmon.manager.FileUploadManager;
import edu.drexel.sd0910.ece01.aqmon.util.ImageUtils;
import edu.drexel.sd0910.ece01.aqmon.util.OSUtils;

/**
 * Wizard - Summary panel. This panel is used to display a summary of
 * information collected using the setup wizard as well as info about the data
 * collection process and where to retrieve the data.
 * 
 * @author Kyle O'Connor
 * 
 */
@SuppressWarnings("serial")
public class SummaryPanel extends JPanel implements Observer, ItemListener {

	private final static Logger log = Logger.getLogger(SummaryPanel.class);

	// gui items
	private JPanel titlePanel;
	private JLabel textLabel;
	private JLabel portLabel;
	private JLabel nodeLabel;
	private JLabel serverLabel;
	
	private JLabel instructionsLabel;
	private JLabel linkToWeb;
	private JLabel linkToLocal;
	
	private JLabel deploymentNameLabel;
	private JComboBox deploymentNameCombo;
	private JLabel deploymentNameStatus;
	
	private JLabel terminalTitleLabel;
	private JTextArea terminal;
	private JScrollPane terminalScrollPane;
	private JCheckBox enableTerminalOutput;

	protected MainModel model;
	
	private static ImageIcon warningIcon = ImageUtils.createImageIcon("/warning.png", "Warning");
	private static ImageIcon noWarningIcon = ImageUtils.createImageIcon("/no_warning.png", "No Warning");
	private static ImageIcon acceptIcon = ImageUtils.createImageIcon("/found.png", "OK");
	private static ImageIcon noAcceptIcon = ImageUtils.createImageIcon("/not_found.png", "Invalid");

	public SummaryPanel(MainModel aModel) {
		this.model = aModel;
		model.addObserver(this);
		initComponents();
	}

	private void initComponents() {
		this.setLayout(new BorderLayout());

		// title bar
		titlePanel = new JPanel(new BorderLayout());
		titlePanel.setBackground(Color.GRAY);
		textLabel = new JLabel();
		JSeparator separator = new JSeparator();
		textLabel.setBackground(Color.GRAY);
		textLabel.setFont(new Font(textLabel.getFont().getFontName(), Font.BOLD, 16));
		textLabel.setText("Deployment Summary");
		textLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		textLabel.setOpaque(true);
		titlePanel.add(textLabel, BorderLayout.CENTER);
		titlePanel.add(separator, BorderLayout.SOUTH);

		add(titlePanel, BorderLayout.NORTH);

		Box summaryBox = new Box(BoxLayout.Y_AXIS);
		summaryBox.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0));

		terminalTitleLabel = new JLabel("Terminal Output: ");
		enableTerminalOutput = new JCheckBox("Enable terminal output (Advanced)");
		enableTerminalOutput.setSelected(false);
		terminal = new JTextArea();
		terminal.setEditable(false);
		terminal.setLineWrap(false);
		terminalScrollPane = new JScrollPane(terminal);
		terminalScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
		terminalScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

		JPanel terminalPane2 = new JPanel(new BorderLayout());
		terminalPane2.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		JPanel terminalPane = new JPanel();
		BoxLayout terminalBox = new BoxLayout(terminalPane, BoxLayout.Y_AXIS);
		terminalPane.setLayout(terminalBox);
		terminalPane.add(terminalScrollPane);
		terminalPane2.add(terminalTitleLabel, BorderLayout.NORTH);
		terminalPane2.add(terminalPane, BorderLayout.CENTER);
		terminalPane2.add(enableTerminalOutput, BorderLayout.SOUTH);

		portLabel = new JLabel("XBee connected on port: ");
		nodeLabel = new JLabel("<html>Deployed nodes: ");
		serverLabel = new JLabel("Connected to server: ");
		deploymentNameLabel = new JLabel("Deployment name: ");
		deploymentNameStatus = new JLabel("Select from above list...", noWarningIcon, JLabel.LEADING);
		deploymentNameStatus.setVerticalTextPosition(JLabel.CENTER);
		deploymentNameStatus.setHorizontalTextPosition(JLabel.RIGHT);
		deploymentNameCombo = new JComboBox();
		deploymentNameCombo.addItemListener(this);
		deploymentNameCombo.setAlignmentX(Component.LEFT_ALIGNMENT);
		deploymentNameCombo.setPreferredSize(new Dimension(250, 25));
		deploymentNameCombo.setMaximumSize(new Dimension(250, 25));
		deploymentNameCombo.setMinimumSize(new Dimension(250, 25));
		initHyperlinkComponents();
		
		summaryBox.add(portLabel);
		summaryBox.add(Box.createVerticalStrut(10));
		summaryBox.add(serverLabel);
		summaryBox.add(Box.createVerticalStrut(10));
		summaryBox.add(nodeLabel);
		summaryBox.add(Box.createVerticalStrut(10));
		summaryBox.add(deploymentNameLabel);
		summaryBox.add(deploymentNameCombo);
		summaryBox.add(Box.createVerticalStrut(5));
		summaryBox.add(deploymentNameStatus);
		summaryBox.add(Box.createVerticalGlue());
		summaryBox.add(linkToWeb);
		summaryBox.add(Box.createVerticalStrut(10));
		summaryBox.add(linkToLocal);

		add(summaryBox, BorderLayout.WEST);
		add(terminalPane2, BorderLayout.CENTER);

		instructionsLabel = new JLabel(
				"Click the 'Finish' button in order to begin the deployment and start collecting data.", JLabel.RIGHT);
		add(instructionsLabel, BorderLayout.SOUTH);
	}

	private void initHyperlinkComponents() {
		linkToWeb = new JLabel("Click here to access the data on the web.");
		linkToWeb.setForeground(Color.BLUE);
		linkToWeb.addMouseListener(new MouseAdapter() {

			@Override
			public void mouseEntered(MouseEvent e) {
				Cursor linkCursor  = new Cursor(Cursor.HAND_CURSOR);
				linkToWeb.setCursor(linkCursor);
			}

			@Override
			public void mouseExited(MouseEvent e) {
				Cursor normalCursor = new Cursor(Cursor.DEFAULT_CURSOR);
				linkToWeb.setCursor(normalCursor);
			}

			@Override
			public void mouseClicked(MouseEvent e) {
				linkToWeb.setForeground(Color.RED);
				OSUtils.browseToURI(model.getProperties().getProperty(ConfigKeys.WEBSITE_URL));
				linkToWeb.setForeground(Color.BLUE);
			}

		});
		linkToLocal = new JLabel("Click here to view the collected data locally.");
		linkToLocal.setForeground(Color.BLUE);
		linkToLocal.addMouseListener(new MouseAdapter() {

			@Override
			public void mouseEntered(MouseEvent e) {
				Cursor linkCursor  = new Cursor(Cursor.HAND_CURSOR);
				linkToLocal.setCursor(linkCursor);
			}

			@Override
			public void mouseExited(MouseEvent e) {
				Cursor normalCursor = new Cursor(Cursor.DEFAULT_CURSOR);
				linkToLocal.setCursor(normalCursor);
			}
			
			@Override
			public void mouseClicked(MouseEvent e) {
				linkToLocal.setForeground(Color.RED);
				OSUtils.browseToURI(FileOutputManager.OUTPUT_FILENAME);
				linkToLocal.setForeground(Color.BLUE);
			}

		});
	}

	public void setDeploymentGuiElementsEnabled(final boolean b) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				deploymentNameStatus.setVisible(false);
				deploymentNameCombo.setEnabled(b);
				//deploymentNameCombo.repaint();
				//deploymentNameStatus.repaint();
			}
		});
	}

	public void addDisableTerminalListener(ActionListener al) {
		enableTerminalOutput.addActionListener(al);
	}

	public void toggleTerminalOutputVisible() {
		terminalScrollPane.setVisible(enableTerminalOutput.isSelected());
		terminalTitleLabel.setVisible(enableTerminalOutput.isSelected());
	}

	private void updateTerminal() {
		// should be called from event dispatch thread (but append IS thread
		// safe)
		if (!javax.swing.SwingUtilities.isEventDispatchThread())
			log.warn("Trying to update status bar outside of event dispatch thread!");

		// clear terminal
		terminal.setText(null);
		// load all data from model into terminal
		List<String> data = model.getTerminalData();
		for (String line : data) {
			terminal.append(line + OSUtils.getLineTerminator());
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
	 */
	public void update(Observable o, Object arg) {
		if (arg instanceof String) {
			//log.debug("Model updated (" + arg + ")");

			if (arg.equals(GUIUpdates.TERMINAL)) {
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						updateTerminal();
					}
				});
			}
		}
	}

	public void displayNode(final ActiveNode an) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				nodeLabel.setText(nodeLabel.getText() + "<br>" + an.getNodeID() + " at (" + an.getLatitude()
						+ ", " + an.getLongitude() + ")");
			}
		});
	}
	
	public void displayNodeFinished() {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				nodeLabel.setText(nodeLabel.getText() + "</html>");
				//nodeLabel.validate();
				//nodeLabel.repaint();
			}
		});
	}

	public void displayPort() {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				portLabel.setText("XBee connected on port: " + model.getPortName());
			}
		});
	}

	public void displayServerStatus() {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				serverLabel.setText("Connected to server: "
						+ ((model.getFileUploadManager().isConnected()) ? "Success" : "Failed"));
			}
		});
	}
	
	public void displayDeploymentNames(final List<String> names) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				for (String name : names) {
					deploymentNameCombo.addItem(name);
				}
				deploymentNameCombo.addItem(FileUploadManager.NEW_DEPLOYMENT_NAME_TYPE);
				//deploymentNameCombo.repaint();
			}
		});
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent)
	 */
	public void itemStateChanged(ItemEvent e) {
		String selectedName = (String) e.getItem();
		if (e.getStateChange() == ItemEvent.SELECTED) {
			if (selectedName.equals(FileUploadManager.NEW_DEPLOYMENT_NAME_TYPE)) {
				deploymentNameStatus.setText("");
				deploymentNameStatus.setIcon(noWarningIcon);
				DeploymentNameCustomDialog cd = new DeploymentNameCustomDialog();
				cd.setVisible(true); // display the dialog
				cd.setLocationRelativeTo(null); // try to center it

				String name = cd.getValidatedText();
				if (name != null) {
					// The text is valid.
					String dir = name.trim(); // always trim off spaces
					model.setDeploymentDirectory(dir);
					((JComboBox) e.getSource()).addItem(dir);
					((JComboBox) e.getSource()).setSelectedItem(dir);
					deploymentNameStatus.setText("A new deployment will be created.");
					deploymentNameStatus.setIcon(acceptIcon);
				} else {
					deploymentNameStatus.setText("A new deployment was not created.");
					deploymentNameStatus.setIcon(noAcceptIcon);
				}
			} else {
				model.setDeploymentDirectory(selectedName);
				deploymentNameStatus.setText("<html>This is an existing deployment.<br>"
						+ "All data will be overwritten.</html>");
				deploymentNameStatus.setIcon(warningIcon);
			}
		}
	}

}
